/***************************************************************************
 * General Ledger, Copyright (C) 2008 Armin Moradi <feng.shaun@gmail.com>  *
 * http://fengshaun.wordpress.com                                          *
 *                                                                         *
 * This program is free software; you can redistribute it and/or modify    *
 * it under the terms of the GNU General Public License as published by    *
 * the Free Software Foundation; either version 3 of the License, or       *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This program is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 * GNU General Public License for more details.                            *
 *                                                                         *
 * You should have received a copy of the GNU General Public License       *
 * along with this program; if not, write to the Free Software             *
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 US  *
 ***************************************************************************/

#include <iostream>

#include <QtGui>

#include "tictactoe.h"

TicTacToe::TicTacToe(QString state, QWidget *parent)
	: QWidget(parent)
{
	setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
	setFocusPolicy(Qt::StrongFocus);

	setState(state);
	setXOnly(false);
	setOOnly(false);
	
	m_penColor = palette().foreground().color();
	m_gridColor = Qt::black;
	m_crossColor = Qt::yellow;
	
	m_image = QImage(100, 100, QImage::Format_RGB32);
	m_image.fill(qRgb(0, 0, 0));

	m_counter = 0;

	connect(this, SIGNAL(filled()), this, SLOT(clear()));
	connect(this, SIGNAL(xWon(int, int, int)), this, SLOT(drawCross(int, int, int)));
	connect(this, SIGNAL(oWon(int, int, int)), this, SLOT(drawCross(int, int, int)));
}


void TicTacToe::paintEvent(QPaintEvent *event) {
	{
		QPainter painter(this);
		painter.setRenderHint(QPainter::Antialiasing);
		painter.setPen(QPen(m_gridColor, 5));
	
		m_image = QImage(this->width() - 1, this->height() - 1, QImage::Format_ARGB32);
	
		for (int i = 2; i < m_image.width(); i += ((m_image.width()) / 3))
			painter.drawLine(i, 0, i, m_image.height());
		painter.drawLine(m_image.width() - 2, 0, m_image.width() - 2, m_image.height());

		for (int j = 2; j < m_image.height(); j += ((m_image.height()) / 3))
			painter.drawLine(0, j, m_image.width() - 1, j);
		painter.drawLine(0, m_image.height() - 2, m_image.width(), m_image.height() - 2);
	}
	drawState();
	if (!m_done)
		checkState();
	if (m_crossIsDrawn)
		drawCross(m_crossPoints[0], m_crossPoints[1], m_crossPoints[2]);
}

void TicTacToe::mousePressEvent(QMouseEvent *event) {
	if (finished() || m_done) {
		emit filled();
		m_done = false;
		m_crossIsDrawn = false;
		return;
	}

	if (m_xOnly) {
		if (player() == 2) {
			return;
		}
	}

	if (m_oOnly) {
		if (player() == 1) {
			return;
		}
	}
	
	if (event->button() == Qt::LeftButton) {
		if (player() == 1) {
			if (!setState('X', event->pos()))
				return;
		} else {
			if (!setState('O', event->pos()))
				return;
		}

		emit playerChanged(player());
	}
}

void TicTacToe::keyPressEvent(QKeyEvent *event) {
	switch(event->key()) {
		case Qt::Key_7:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(0, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(0, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_8:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(1, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(1, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_9:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(2, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(2, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_4:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(3, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(3, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_5:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(4, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(4, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_6:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(5, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(5, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_1:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(6, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(6, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_2:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(7, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(7, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_3:
			if (finished() || m_done) {
				emit filled();
				m_done = false;
				return;
			}
			if (player() == 1) {
				if (setState(8, 'X'))
					emit playerChanged(player());
			} else {
				if (setState(8, 'O'))
					emit playerChanged(player());
			}
			break;
		case Qt::Key_0:
			clear();
			emit playerChanged(player());
			break;
		default:
			QWidget::keyPressEvent(event);
			break;
	}
}

bool TicTacToe::setState(QChar letter, const QPoint &pos) {
	for (int i = 0; i < 9; i++) {
		if (pixelRect(i).contains(pos, true)) {
			if (letter == 'X') {
				if (!setState(i, 'X'))
					return false;
			} else if (letter == 'O') {
				if (!setState(i, 'O'))
					return false;
			}
		}
	}
	emit stateChanged(state());
	update();
	return true;
}

bool TicTacToe::setState(int pos, QChar letter) {
	if (state(pos) == '-') {
		m_state[pos] = letter;
		m_counter++;
		emit stateChanged(state());
		emit playerChanged(player());
		emit stateUpdated(pos);
		update();
		return true;
	}
	return false;
}

bool TicTacToe::setState_suppress(int pos, QChar letter) {
	if (state(pos) == '-') {
		m_state[pos] = letter;
		m_counter++;
		emit playerChanged(player());
		update();
		return true;
	}
	return false;
}

void TicTacToe::setState(const QString state) {
	m_state = state;
	update();
}

QRect TicTacToe::pixelRect(const int i, const int j) {
	int x = i * (m_image.width() / 3);
	int y = j * (m_image.height() / 3);

	return QRect (x, y, m_image.width() / 3, m_image.height() / 3);
}

QRect TicTacToe::pixelRect(const int i) {
	switch(i) {
		case 0:
			return pixelRect(0, 0);
			break;
		case 1:
			return pixelRect(1, 0);
			break;
		case 2:
			return pixelRect(2, 0);
			break;
		case 3:
			return pixelRect(0, 1);
			break;
		case 4:
			return pixelRect(1, 1);
			break;
		case 5:
			return pixelRect(2, 1);
			break;
		case 6:
			return pixelRect(0, 2);
			break;
		case 7:
			return pixelRect(1, 2);
			break;
		case 8:
			return pixelRect(2, 2);
			break;
		default:
			break;
	}
}

void TicTacToe::drawX(const QRect &rect) {
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing);
	painter.setPen(QPen(m_penColor, 10));

	QRect rect2 = rect;
	rect2.setSize(rect.size() - QSize(40, 40));
	rect2.translate(QPoint(20, 20));
	
	painter.drawLine(rect2.topLeft(), rect2.bottomRight());
	painter.drawLine(rect2.topRight(), rect2.bottomLeft());
}

void TicTacToe::drawO(const QRect &rect) {
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing);
	painter.setPen(QPen(m_penColor, 10));

	QRect rect2 = rect;
	rect2.setSize(rect.size() - QSize(40, 40));
	rect2.translate(QPoint(20, 20));

	painter.drawEllipse(rect2);
}

void TicTacToe::clear() {
	clear_suppress();
	emit stateUpdated(10);
}

void TicTacToe::clear_suppress() {
	setState(QString("---------"));
	m_counter = 0;
	m_done = false;
	m_crossIsDrawn = false;
	update();
}

QSize TicTacToe::sizeHint() {
	return m_image.size();
}

void TicTacToe::drawState() {
	for (int k = 0; k < 9; k++) {
		if (state(k) == 'X') {
			switch(k) {
				case 0:
					drawX(pixelRect(0, 0));
					break;
				case 1:
					drawX(pixelRect(1, 0));
					break;
				case 2:
					drawX(pixelRect(2, 0));
					break;
				case 3:
					drawX(pixelRect(0, 1));
					break;
				case 4:
					drawX(pixelRect(1, 1));
					break;
				case 5:
					drawX(pixelRect(2, 1));
					break;
				case 6:
					drawX(pixelRect(0, 2));
					break;
				case 7:
					drawX(pixelRect(1, 2));
					break;
				case 8:
					drawX(pixelRect(2, 2));
					break;
				default:
					break;
			}
		} else if (state(k) == 'O') {
			switch(k) {
				case 0:
					drawO(pixelRect(0, 0));
					break;
				case 1:
					drawO(pixelRect(1, 0));
					break;
				case 2:
					drawO(pixelRect(2, 0));
					break;
				case 3:
					drawO(pixelRect(0, 1));
					break;
				case 4:
					drawO(pixelRect(1, 1));
					break;
				case 5:
					drawO(pixelRect(2, 1));
					break;
				case 6:
					drawO(pixelRect(0, 2));
					break;
				case 7:
					drawO(pixelRect(1, 2));
					break;
				case 8:
					drawO(pixelRect(2, 2));
					break;
				default:
					break;
			}
		}
	}
}

QChar TicTacToe::state(int pos) {
	return m_state[pos];
}

QString TicTacToe::state() {
	return m_state;
}

void TicTacToe::printState() {
	for (int i = 0; i < 9; i++) {
		if (state(i) == 'X') {
			std::cout << 'X';
		} else if (state(i) == 'O') {
			std::cout << 'O';
		} else {
			std::cout << '-';
		}
	}
}

bool TicTacToe::finished() {
	for (int i = 0; i < 9; i++) {
		if (state(i) == '-') {
			return false;
		}
	}
	return true;
}

void TicTacToe::setColors(QColor gridColor, QColor penColor, QColor crossColor) {
	m_penColor = penColor;
	m_gridColor = gridColor;
	m_crossColor = crossColor;
}

bool TicTacToe::statesAreEqual(int a, int b, int c, char l) {
	if (state(a) == state(b) &&
		state(b) == state(c) &&
		state(a) == l)
	{
		return true;
	}
	return false;
}

void TicTacToe::checkState() {
	if (statesAreEqual(0, 1, 2, 'X')) {
		m_done = true;
		emit xWon(0, 1, 2);
		return;
	} else if (statesAreEqual(3, 4, 5, 'X')) {
		m_done = true;
		emit xWon(3, 4, 5);
		return;
	} else if (statesAreEqual(6, 7, 8, 'X')) {
		m_done = true;
		emit xWon(6, 7, 8);
		return;
	} else if (statesAreEqual(0, 3, 6, 'X')) {
		m_done = true;
		emit xWon(0, 3, 6);
		return;
	} else if (statesAreEqual(1, 4, 7, 'X')) {
		m_done = true;
		emit xWon(1, 4, 7);
		return;
	} else if (statesAreEqual(2, 5, 8, 'X')) {
		m_done = true;
		emit xWon(2, 5, 8);
		return;
	} else if (statesAreEqual(2, 4, 6, 'X')) {
		m_done = true;
		emit xWon(2, 4, 6);
		return;
	} else if (statesAreEqual(0, 4, 8, 'X')) {
		m_done = true;
		emit xWon(0, 4, 8);
		return;
	}

	else if (statesAreEqual(0, 1, 2, 'O')) {
		m_done = true;
		emit oWon(0, 1, 2);
		return;
	} else if (statesAreEqual(3, 4, 5, 'O')) {
		m_done = true;
		emit oWon(3, 4, 5);
		return;
	} else if (statesAreEqual(6, 7, 8, 'O')) {
		m_done = true;
		emit oWon(6, 7, 8);
		return;
	} else if (statesAreEqual(0, 3, 6, 'O')) {
		m_done = true;
		emit oWon(0, 3, 6);
		return;
	} else if (statesAreEqual(1, 4, 7, 'O')) {
		m_done = true;
		emit oWon(1, 4, 7);
		return;
	} else if (statesAreEqual(2, 5, 8, 'O')) {
		m_done = true;
		emit oWon(2, 5, 8);
		return;
	} else if (statesAreEqual(2, 4, 6, 'O')) {
		m_done = true;
		emit oWon(2, 4, 6);
		return;
	} else if (statesAreEqual(0, 4, 8, 'O')) {
		m_done = true;
		emit oWon(0, 4, 8);
		return;
	}
}

void TicTacToe::drawCross(int a, int b, int c) {
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing);
	painter.setPen(QPen(m_crossColor, 5));

	painter.drawLine(pixelRect(a).center(), pixelRect(c).center());
	m_done = true;
	m_crossIsDrawn = true;
	m_crossPoints[0] = a;
	m_crossPoints[1] = b;
	m_crossPoints[2] = c;
}

QColor TicTacToe::gridColor() const {
	return m_gridColor;
}

QColor TicTacToe::penColor() const {
	return m_penColor;
}

QColor TicTacToe::crossColor() const {
	return m_crossColor;
}

void TicTacToe::setGridColor(QColor c) {
	m_gridColor = c;
}

void TicTacToe::setPenColor(QColor c) {
	m_penColor = c;
}

void TicTacToe::setCrossColor(QColor c) {
	m_crossColor = c;
}

int TicTacToe::player() {
	if (m_counter % 2 == 0) {
		return 1;
	} else {
		return 2;
	}
}

void TicTacToe::setXOnly(bool yes) {
	m_xOnly = yes;
}

void TicTacToe::setOOnly(bool yes) {
	m_oOnly = yes;
}
